from graphql.error import GraphQLError


def clear_traceback_locals(tb):
    """Clear all locals from all connected frames.

    Similar to `clear_frames()` method of the `traceback` module but clears locals from all frames in tracebacks.
    """
    frame = tb.tb_frame
    while frame:
        try:
            frame.clear()
        except RuntimeError:
            # Ignore the exception raised if the frame is still executing.
            break
        frame = frame.f_back


def clear_exception_locals(exception):
    # Clear locals from the exception.
    if exception.__traceback__:
        clear_traceback_locals(exception.__traceback__)
    # Clear exception stored as the `__cause__` of processed exception.
    if exception.__cause__:
        clear_exception_locals(exception.__cause__)
    # Clear exception stored as the `original_error` of processed exception.
    # This is used in `GraphQLError` from `GraphQL-Core-Legacy`.
    if isinstance(exception, GraphQLError) and hasattr(exception, "original_error"):
        clear_exception_locals(exception.original_error)


def clear_errors(errors):
    """Clear locals from exceptions in errors.

    We can't to use the `clear_frames()` method of the `traceback` module. This function uses `tb_next` attribute
    to clear all locals from all frames in tracebacks. Errors generated by `GraphQL-Core-Legacy` don't have
    properly connected tracebacks, so we need to clear locals that go through frames.
    """
    for error in errors:
        clear_exception_locals(error)
